# -*- coding:utf-8 -*-

import csv;
import logging;
import sys;
from configparser import ConfigParser;
__all__ = [
    # Classes
    'SvnSubmin'
];

'''
CREATE TABLE users
                (
                        id       integer primary key autoincrement,
                        name     text not null unique,
                        password text not null,
                        email    text,
                        fullname text,
                        is_admin bool default 0
                );

CREATE TABLE groups
                (
                        id   integer primary key autoincrement,
                        name text not null unique
                );

CREATE TABLE group_members
                (
                        groupid integer not null references groups(id),
                        userid  integer not null references user(id),
                        PRIMARY KEY(groupid, userid)
                );

CREATE TABLE "permissions" (
                        repository     text,
                        repositorytype text,
                        path           text not null,
                        subjecttype    text not null,   -- user, group or all
                        subjectid      integer,         -- only null if subjecttype is all
                        type           text default '', -- '', 'r' or 'rw'
                        UNIQUE(repository, repositorytype, path, subjecttype, subjectid)
                );

'''

class SvnUser:
    __template__ = "insert into users(name, password, email, fullname, is_admin) values('{0}', '{1}', '{2}', '{3}', {4});";
    __type__ = 'user';
    
    def __init__(self, username = None, password = None):
        self.username = username;
        self.password = password;
        self.email = username + '@submine.com';
        self.fullname = username;

    def name(self):
        return self.username;

    def type(self):
        return SvnUser.__type__;

    def to_sql(self):
        print(SvnUser.__template__.format( self.username, self.password, self.email, self.fullname, '0'));

    def find_id(self):
        return "(select id from users where name = '{0}')".format(self.username);
        
class SvnGroup:
    __type__ = 'group';
    def __init__(self, group_name):
        '''
        
        @param group_name
        @param users 
        '''
        self.group_name = group_name;
        self.__users__ = {};
        # logging.warning("new group with name [%s]", group_name);

    def type(self):
        return self.__type__;

    def name(self):
        return self.group_name;
        
    def add(self, user):
        '''
        add user to group members
        @param user
        '''
        self.__users__[user.username] = user;

    def add_users(self, tobe_add_users):
        '''
        @param users
        '''
        self.__users__.update(tobe_add_users);

    def remove(self, user_name):
        '''
        remove a group member 
        @param user_name the username to be removed from group member
        '''
        if user_name in self.__users__:
            del self.__users__[user_name];

    def users(self):
        return self.__users__.values();

    def to_sql(self):
        print("insert into groups(name) values('{}');".format( self.group_name));
        for user in self.__users__.values():
            print("insert into group_members(groupid, userid) values((select id from groups where name = '{0}'), (select id from users where name = '{1}'));".format(self.group_name, user.username ));

    def find_id(self):
        return "(select id from groups where name = '{}')".format(self.group_name);

class SvnAll:
    __type__ = 'all';
    def __init__(self):
        pass;

    def name(self):
        return 'all';
    def type(self):
        return SvnAll.__type__;

    def find_id(self):
        return 'null';

class SvnPermissionRule:
    def __init__(self, subject = None, access = None):
        self.__subject__ = subject;
        self.access = access;
        # logging.warning('new rule, access is [%s]', access);

class SvnPermission:
    def __init__(self, repository, path):
        self.__repository__ = repository;
        self.__path__ = path;
        self.__permissions__ = [];
        
    def add_rule(self, rule):
        '''
        
        @param rule
        '''
        self.__permissions__.append(rule);
        
    def to_sql(self):
        '''
        
        '''
        for pm_rule in self.__permissions__:
            print("insert into permissions( repository, repositorytype, path, subjecttype, subjectid, type) values ('{0}', '{1}', '{2}', '{3}', {4}, '{5}');".format( self.__repository__, 'svn', self.__path__, pm_rule.__subject__.__type__, pm_rule.__subject__.find_id(), pm_rule.access));
            

class SvnSubmin:
    '''
    '''
    def __init__(self, authz = None, access = None, target = None):
        self.authz = authz;
        self.access = access;
        self.target = None;
        self.__users__ = {};
        self.groups = {};
        self.permissions = {};
        self.parse_authz();
        self.parse_access();
        
    def parse_authz(self):
        if self.authz == None:
            return None;
        authz_reader = csv.reader(open(self.authz), delimiter = ':');
        for user in authz_reader:
            if len( user ) <2 :
                continue;
            self.__users__[user[0]] = SvnUser(user[0], user[1]);

    def __find_group_members__(self, group_name):
        '''
        '''
        if not group_name in self.groups:
            logging.warning("group [%s] doesn't exist.", group_name);
            return;
        checked_groups = set();
        users = {};
        to_be_check_groups = set();
        to_be_check_groups.add(group_name);
        while len(to_be_check_groups) > 0:
            to_be_check_group_name = to_be_check_groups.pop();
            if to_be_check_group_name in checked_groups:
                continue;
            checked_groups.add(to_be_check_group_name);
            for user in self.groups[group_name].users():
                if not user.username.startswith('@'):
                    users[user.username] = user;
                if not user.username[1:] in checked_groups:
                    to_be_check_groups.add(user.username[1:]);
                
        return users;

    def __parse_group_ref__(self):
        '''
        解析组对组的引用
        '''
        for group in self.groups.values():
            # logging.debug('group type is %s', type(group.users()));
            for user in [x for x in group.users()]:
                if not user.username.startswith('@'):
                    continue;
                group.remove(user.username);
                group.add_users(self.__find_group_members__(user.username[1:]));

    def __find_users__(self, usernames):
        us = {};
        for origin_username in usernames:
            username = origin_username.strip();
            if len(username) == 0:
                continue;
            if username.startswith('@') :
                us[username] = SvnUser( username, '');
            elif username in self.__users__:
                us[username] = self.__users__[username];
            else:
                logging.warn('user[%s] doesn\'t exist', username);
        return us;
    
    def parse_access(self):
        '''
        parse apache svn access control file
        '''
        config = ConfigParser();
        config.read([ self.access ], encoding = 'UTF-8');
        for group in config.items('groups'):
            self.groups[group[0]] = SvnGroup(group[0]);
            self.groups[group[0]].add_users( self.__find_users__( group[1].split(',')));

        self.__parse_group_ref__();
        
        for url in config.sections():
            if url == 'groups':
                continue;
            idx = url.find(':');
            if idx <= 0:
                continue;
            repository = url[:idx];
            path = url[idx + 1:];
            permission = SvnPermission(repository, path);
            self.permissions[url] = permission;
            for item in config.items(url):
                subject = self.__find_subject__(item[0]);
                if subject == None:
                    logging.error('config error, subject [%s] doesn\'t exist', item[0][1:]);
                    continue;
                # logging.warning('access is [%s]', item[1]);
                rule = SvnPermissionRule(subject = subject, access = item[1]);
                permission.add_rule( rule );
        
    def __find_subject__(self, name):
        try:
            if name.startswith('@'):
                return self.groups[name[1:]];
            elif name == '*':
                return SvnAll();
            else:
                return self.__users__[name];
        except KeyError:
            return None;

    def to_sql(self):
        for user in self.__users__.values():
            user.to_sql();
        for group in self.groups.values():
            group.to_sql();
        for pm in self.permissions.values():
            pm.to_sql();
        
    def convert(self, target = None):
        pass;

if __name__ == "__main__":
    # def __init__(self, authz = None, access = None, target = None):    
    svn_submin = SvnSubmin( authz = './svn-apache-authz', access = './svn-apache-access', target = './svn-submin.sqlite.sql');
    svn_submin.to_sql();

